package com.ljs.liojbackendjudgeservice.judge;

import cn.hutool.json.JSONUtil;

import com.ljs.libackendmodel.model.codesandbox.ExecuteCodeRequest;
import com.ljs.libackendmodel.model.codesandbox.ExecuteCodeResponse;
import com.ljs.libackendmodel.model.codesandbox.QuestionJudgeInfo;
import com.ljs.libackendmodel.model.dto.question.QuestionJudgeCase;
import com.ljs.libackendmodel.model.entity.Question;
import com.ljs.libackendmodel.model.entity.QuestionSubmit;
import com.ljs.libackendmodel.model.enums.QuestionSubmitStatusEnum;
import com.ljs.liojbackendcommon.common.ErrorCode;
import com.ljs.liojbackendcommon.exception.BusinessException;
import com.ljs.liojbackendjudgeservice.judge.codesandbox.CodeSandbox;
import com.ljs.liojbackendjudgeservice.judge.codesandbox.CodeSandboxFactory;
import com.ljs.liojbackendjudgeservice.judge.codesandbox.CodeSandboxProxy;
import com.ljs.liojbackendjudgeservice.judge.strategy.JudgeContext;
import com.ljs.liojbackendserviceclient.service.QuestionFeignClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class JudgeServiceImpl implements JudgeService {
    @Value("${codesandbox.type:example}")
    private String type;
    @Resource
    private QuestionFeignClient questionFeignClient;

    @Resource
    private JudgeManage judgeManage;

    @Override
    public QuestionSubmit doJudge(Long questionSubmitId) {
        // 1)传入题日的提交 id，获取到对应的题目、提交信息《包含代码、编程语兰等)
        QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);
        if (questionSubmitId == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "提交信息不存在");
        }
        Long questionId = questionSubmit.getQuestionId();
        Question question = questionFeignClient.getQuestionById(questionId);
        if (question == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");
        }
//        2)如果题目提交状态不为等待中，就不用重复执行了
        if (!questionSubmit.getStatus().equals(QuestionSubmitStatusEnum.WAITING.getValue())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "操作失败已在判题中");
        }
        //3)更改判题(题目提交)的状态为“判题中'
        QuestionSubmit questionSubmitUpdate = new QuestionSubmit();
        questionSubmitUpdate.setId(questionSubmitId);
        questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());
        boolean updateResult = questionFeignClient.questionSubmitSUpdateById(questionSubmitUpdate);
        if (!updateResult) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新状态失败");
        }

        //4)调用沙箱，获取到执行结果
        CodeSandbox codeSandbox = CodeSandboxFactory.newInstance(type);
        codeSandbox = new CodeSandboxProxy(codeSandbox);
        String code = questionSubmit.getCode();
        String language = questionSubmit.getLanguage();
        String judgeCaseStr = question.getJudgeCase();
        List<QuestionJudgeCase> judgeCaseList = JSONUtil.toList(judgeCaseStr, QuestionJudgeCase.class);
        List<String> inputList = judgeCaseList.stream().map(QuestionJudgeCase::getInput).collect(Collectors.toList());

        ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
                .code(code)
                .inputLists(inputList)
                .language(language)
                .build();
        ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
        List<String> outputLists = executeCodeResponse.getOutputLists();


        //5)根据沙箱的执行结果，设置题口的判题状态和信息
        JudgeContext judgeContext = new JudgeContext();
        judgeContext.setJudgeInfo(executeCodeResponse.getQuestionJudgeInfo());
        judgeContext.setInputList(inputList);
        judgeContext.setJudgeCaseList(judgeCaseList);
        judgeContext.setQuestionSubmit(questionSubmit);
        judgeContext.setOutputList(outputLists);
        judgeContext.setQuestion(question);


        //6)选择策略
        QuestionJudgeInfo judgeInfo = judgeManage.doJudge(judgeContext);


        //7)修改数据库中的判题结果
        questionSubmitUpdate=new QuestionSubmit();
        questionSubmitUpdate.setId(questionSubmitId);
        questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());
        questionSubmitUpdate.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));
        updateResult = questionFeignClient.questionSubmitSUpdateById(questionSubmitUpdate);
        if (!updateResult) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新状态失败");
        }
        return questionFeignClient.getQuestionSubmitById(questionSubmitId);

        /*
        我们的判题策略可能会有很多种，比如:我们的代码沙箱本身执行程序需要消耗时间，这个时间可能不同的编程言是不同的，比如沙箱执行 Java 要额外花 10 秒。
        我们可以采用策略模式，针对不同的情况，定义独立的策略，而不是把所有的判题逻辑、if。。。else 代码全部
        混在一起写。
        */
    }
}